package simple3D.core 
{
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.geom.Matrix3D;
	import flash.geom.Orientation3D;
	import flash.geom.Point;
	import flash.geom.Vector3D;
	import simple3D.materials.Material3D;
	import simple3D.utils.MathUtil;
	/**
	 * ...
	 * @author Dmitriy Gaiduk
	 */
	public class DisplayObject3D extends Sprite
	{
		public var adjustedCentr:Vector3D;
		static public const FOCAL_LENGTH:Number = 5200;
		public var angle:Vector3D;
		public var position:Vector3D;
		public var scale:Vector3D;
		public var centrPoint:Vector3D = new Vector3D();
		public var distance:Number = 0;
		protected var parent3D:DisplayObject3D;
		
		public var innerMatrix:Matrix3D;
		
		public function DisplayObject3D() 
		{
			super();
			//initial settings
			angle = new Vector3D(0, 0);
			position = new Vector3D(0, 0, 0);
			scale = new Vector3D(1, 1, 1);
			innerMatrix = new Matrix3D();
		}
		
		public function setCentrPoint(x:Number, y:Number, z:Number):void
		{
			centrPoint.x = x;
			centrPoint.y = y;
			centrPoint.z = z;
		}
		
		public function moveTo(p:Vector3D):void
		{
			position = p.clone();
		}
		
		public function update():void
		{
			// update childs
			var do3D:DisplayObject3D;
			var childNum:int = this.numChildren;
			if (parent != null && parent is DisplayObject3D)
			{
				
			}else
			{
				var v:Vector.<Vector3D> = new Vector.<Vector3D>();
				v.push(position, angle, scale);
				innerMatrix = new Matrix3D();
				innerMatrix.recompose(v, Orientation3D.EULER_ANGLES);
			}
			// calculate position and rotation for childs
			for (var i:uint=0;i<childNum;i++)
			{
				if (this.getChildAt(i) is DisplayObject3D)
				{
					do3D = this.getChildAt(i) as DisplayObject3D;
					do3D.appendMatrix(innerMatrix);
					do3D.update();
				}
				
			}
			
			// calc distance
			adjustedCentr = rotateAndTranslate(centrPoint);
			
			// may occur wrong sorting, must be implemented position of view
			distance = adjustedCentr.z;
			
			// sort by Z
			sortDepths();
			updateView();
		}
		
		public function rotateAndTranslate(v:Vector3D):Vector3D
		{
			return innerMatrix.transformVector(v);
			/*var adjustedV:Vector3D = MathUtil.rotateXYZ(v, angle);
			adjustedV.incrementBy(position);
			if ((parent != null) && (parent is DisplayObject3D))
			{
				var par:DisplayObject3D = parent as DisplayObject3D;
				return par.rotateAndTranslate(adjustedV);
			}else
			{
				return adjustedV;
			}*/
		}
		
		public function updateView():void
		{
			
		}
		
		public function appendMatrix(matr:Matrix3D):void
		{
			var v:Vector.<Vector3D> = new Vector.<Vector3D>();
			v.push(position, angle, scale);
			innerMatrix = new Matrix3D();
			innerMatrix.recompose(v, Orientation3D.EULER_ANGLES);
			innerMatrix.append(matr);
		}
		
		protected function drawCentrPoint():void
		{
			adjustedCentr = rotateAndTranslate(centrPoint);
			var perspectiveCentr:Number =calcPerspective(adjustedCentr.z);
			distance = adjustedCentr.z;
			// draw centr point
			var vertice:Point = new Point(adjustedCentr.x * perspectiveCentr, adjustedCentr.y * perspectiveCentr);
			graphics.beginFill(0xFF0000);
			graphics.lineStyle(1, 0x000000);
			graphics.drawCircle(vertice.x, vertice.y, 7);
			graphics.endFill();
		}
		
		public function getPointOnScreen(v:Vector3D):Point
		{
			var adjustedV:Vector3D = rotateAndTranslate(v);
			var perspective:Number =calcPerspective(adjustedV.z);
			// draw centr point
			return new Point(adjustedV.x * perspective, adjustedV.y * perspective);
		}
		
		public function calcPerspective(z:Number):Number
		{
			return 1;
			//return FOCAL_LENGTH / (FOCAL_LENGTH + z);
		}
		
		/*protected function transformPointLocal(v:Vector3D):Vector3D
		{
			var adjustedV:Vector3D = MathUtil.rotateXYZ(v, angle);
				adjustedV.x *= scale.x;
				adjustedV.y *= scale.y;
				adjustedV.z *= scale.z;
				//adjust for position
				//adjustedV.incrementBy(positionWorld);
				// calculate if parent
				if (parent3D != null)
				{
					adjustedV = parent3D.rotateAndTranslate(adjustedV);
				}
			return adjustedV
		}*/
		
		public function sortDepths():void
		{
			//array to store references to shapes
			var shapes:Array = [];
			//get number of objects in the display list
			var len:uint = this.numChildren;
			//loop thru objects and add Shape3D objects to our array
			for (var i:uint=0;i<len;i++)
			{
				if (this.getChildAt(i) is DisplayObject3D)
					shapes.push(this.getChildAt(i));
			}
			//sort the array based on the z position of the objects
			shapes = shapes.sortOn(["distance"], Array.NUMERIC|Array.DESCENDING);
			//move each object in turn to the top (front) of the display list
			len = shapes.length;
			for (i=0;i<len;i++)
				this.addChild(shapes[i]);
		}
		
		override public function addChild(child:DisplayObject):flash.display.DisplayObject 
		{
			var addedChild:DisplayObject = super.addChild(child);
			if (addedChild is DisplayObject3D)
			{
				var ch:DisplayObject3D = addedChild as DisplayObject3D;
				ch.parent3D = this;
			}
			return addedChild;
		}
		
		public function globalToLocal3D2(v:Vector3D):Vector3D
		{
			var matr:Matrix3D = innerMatrix.clone();
			//matr.transpose();
			if (matr.invert())
			{
				
				return matr.transformVector(v);
			}else
			{
				return null;
			}
		}
		
		override public function removeChild(child:DisplayObject):flash.display.DisplayObject 
		{
			var removedChild:DisplayObject =  super.removeChild(child);
			if (removedChild is DisplayObject3D)
			{
				var ch:DisplayObject3D = removedChild as DisplayObject3D;
				ch.parent3D = this;
			}
			return removedChild;
		}
		
	}

}